<template>
  <div class="chat-view">
    <!-- 统一导航栏 -->
    <NavBar @toggle-sidebar="toggleSidebar" />

    <div class="chat-container">
      <!-- Left Sidebar - Chat History -->
      <div class="sidebar" :class="{ collapsed: sidebarCollapsed }">
      <!-- Sidebar Header -->
      <div class="sidebar-header">
        <h2 class="sidebar-title">聊天历史</h2>
        <div class="search-box">
          <div class="search-icon">
            <div class="search-symbol"></div>
          </div>
          <input
            type="text"
            v-model="searchQuery"
            class="search-input"
            placeholder="搜索对话..."
          />
        </div>
      </div>

      <!-- Chat List -->
      <div class="chat-list">
        <div
          v-for="chat in filteredChats"
          :key="chat.id"
          class="chat-item"
          :class="{ active: activeChat?.id === chat.id }"
          @click="selectChat(chat)"
        >
          <div class="chat-item-header">
            <h3 class="chat-title">{{ chat.title }}</h3>
            <span class="chat-time">{{ chat.time }}</span>
          </div>
          <p class="chat-preview">{{ chat.preview }}</p>
        </div>
      </div>

      <!-- New Chat Button -->
      <div class="sidebar-footer">
        <button class="new-chat-btn" @click="createNewChat">
          新建对话
        </button>
      </div>
    </div>

    <!-- Main Chat Area -->
    <div class="chat-main">
      <!-- Chat Header -->
      <div class="chat-header">
        <div class="chat-info">
          <div class="chat-avatar">
            <el-icon><HelpFilled /></el-icon>
          </div>
          <div class="chat-details">
            <h2 class="chat-name">AI 助手</h2>
            <p class="chat-status">在线</p>
          </div>
        </div>
        <div class="chat-actions">
        </div>
      </div>

      <!-- Messages Container -->
      <div class="messages-container" ref="messagesContainer">
        <div
          v-for="message in messages"
          :key="message.id"
          class="message"
          :class="{ 'user-message': message.isUser, 'bot-message': !message.isUser }"
          :data-streaming="message.isStreaming"
        >
            <div class="message-content">
              <div
                  v-if="message.isUser"
                  class="message-text"
              >
                {{ message.text }}
              </div>
              <div
                  v-else-if="!message.isUser && !message.imageUrl && !message.videoUrl && !message.audioUrl"
                  class="message-text markdown-content"
                  v-html="message.htmlContent || message.text"
              ></div>
              <div v-else-if="message.imageUrl">
                <img
                    :src="message.imageUrl"
                    alt="Generated Image"
                    class="generated-image"
                    @click="downloadImage(message.imageUrl)"
                >
              </div>
              <div v-else-if="message.videoUrl">
                <video
                    :src="message.videoUrl"
                    controls
                    class="generated-video"
                    @click="downloadVideo(message.videoUrl)"
                ></video>
              </div>
              <!-- 音频播放器 -->
              <div v-if="message.audioUrl">
                <div class="audio-container">
                  <audio
                      :src="message.audioUrl"
                      controls
                      class="generated-audio"
                      preload="metadata"
                  >
                    您的浏览器不支持音频播放。
                  </audio>
                  <button
                      class="download-audio-btn"
                      @click="downloadAudio(message.audioUrl)"
                      title="下载音频文件"
                  >
                    📥 下载音频
                  </button>
                </div>
              </div>
            </div>
            <!-- 习题保存按钮 -->
            <div v-if="!message.isUser && message.questionData" class="question-actions">
              <button
                class="save-question-btn"
                @click="saveQuestion(message.questionData)"
              >
                💾 保存题目
              </button>
            </div>
            <div
              v-else-if="!message.isUser && message.isDocument " class="lesson-plan-download-btn-container">
                <button
                    class="download-doc-btn"
                    @click="downloadLessonPlan(message)"
                >
                  📄 下载教案
                </button>
            </div>
            <span class="message-time">{{ message.time }}</span>
          </div>
      </div>

      <!-- Chat Input -->
      <div class="chat-input-container">
        <!-- Chat Mode Toggle -->
        <div class="chat-modes">
          <button
            class="mode-btn"
            :class="{ active: chatMode === 'normal' }"
            @click="setChatMode('normal')"
          >
            <el-icon><ChatDotRound /></el-icon>
            <span>普通对话</span>
          </button>
        </div>

        <!-- Input Box -->
        <div class="input-box">
          <input
            type="text"
            v-model="inputMessage"
            class="message-input"
            placeholder="输入您的消息..."
            @keypress.enter="sendMessage"
            :disabled="isLoading"
          />

          <!-- 录音按钮 -->
          <button
            class="record-btn"
            @click="isRecording ? stopRecording() : startRecording()"
            :class="{ recording: isRecording }"
            :disabled="isLoading"
            :title="isRecording ? '点击停止录音' : '点击开始录音'"
          >
            <div class="record-icon" :class="{ active: isRecording }">
             <el-icon><Microphone /></el-icon>
              <div v-if="isRecording" class="sound-waves">
                <div class="wave wave1"></div>
                <div class="wave wave2"></div>
                <div class="wave wave3"></div>
              </div>
            </div>
            <span v-if="isRecording" class="recording-time">{{ formatRecordingTime(recordingTime) }}</span>
          </button>

          <button
            class="send-btn"
            @click="sendMessage"
            :disabled="!inputMessage.trim() || isLoading"
          >
            <el-icon><Promotion /></el-icon>
          </button>
        </div>
      </div>
    </div>
  </div>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, computed, nextTick, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { marked } from 'marked'
import hljs from 'highlight.js'
import NavBar from '@/components/NavBar.vue'

// 侧边栏状态
const sidebarCollapsed = ref(false)

// 切换侧边栏
const toggleSidebar = () => {
  sidebarCollapsed.value = !sidebarCollapsed.value
}
import 'highlight.js/styles/github.css'
import {
  addChatMessage,
  addChatSession,
  getChatCompletionAPI,
  getChatSessionsByUserId,
  getChatMessagesByChatId,
  addQuestionAPI,
  generateImageAPI,
  generateVideoAPI,
  getVideoStatusAPI,
  textToSpeechAPI,
  speechToTextAPI
} from "@/apis";
import {Document,Packer,Paragraph,TextRun} from "docx";
import {saveAs} from "file-saver";
import {uploadFileByUrlAPI, uploadFileByUrlWithEncodingAPI} from "@/apis/fileAPI/uploadFileByUrlAPI.ts";
import {uploadFileAPI} from "@/apis/fileAPI/uploadFileAPI.ts";
import {uploadAiResourceAPI} from "@/apis/ai-resourceAPI/uploadAIResourceAPI.ts";
import { Document as DocumentIcon } from '@element-plus/icons-vue'
const router = useRouter()

// 配置Markdown渲染器
marked.setOptions({
  breaks: true,
  gfm: true
})

// 自定义渲染器用于代码高亮
const renderer = new marked.Renderer()
renderer.code = function(token: any) {
  const code = token.text
  const language = token.lang
  
  if (language && hljs.getLanguage(language)) {
    try {
      const highlighted = hljs.highlight(code, { language }).value
      return `<pre><code class="hljs language-${language}">${highlighted}</code></pre>`
    } catch (err) {
      console.error('代码高亮错误:', err)
    }
  }
  const highlighted = hljs.highlightAuto(code).value
  return `<pre><code class="hljs">${highlighted}</code></pre>`
}

marked.use({ renderer })

// 响应式数据
const searchQuery = ref('')
const inputMessage = ref('')
const isLoading = ref(false)
const chatMode = ref<'normal' | 'exercise' | 'image'|'video'|'plan'|'audio'>('normal')
const messagesContainer = ref<HTMLElement>()
const isFirstMessage = ref(true)
const currentSessionId = ref<number | null>(null)
const currentUserId = ref<number | null>(null)

// 语音录制相关状态
const isRecording = ref(false)
const mediaRecorder = ref<MediaRecorder | null>(null)
const audioChunks = ref<Blob[]>([])
const recordingTime = ref(0)
const recordingTimer = ref<NodeJS.Timeout | null>(null)

// 聊天数据
onMounted(() => {
  const userId = localStorage.getItem('id')
  if (userId) {
    currentUserId.value = Number(userId)
    loadChatHistory()
  } else {
    console.error("未获取到用户ID")
  }
  //
  const savedSessionId = localStorage.getItem('currentSessionId');
  scrollToBottom()
})

// 组件销毁时清理录音资源
onUnmounted(() => {
  cleanup()
})
const chats = reactive<ChatItem[]>([])

const messages = reactive<MessageItem[]>([
  {
    id: 1,
    text: '你好！我是你的AI助手，有什么可以帮助你的吗？',
    time: '10:30 AM',
    isUser: false
  },
])

const activeChat = ref<ChatItem | null>(null)
const userAvatar = ref('https://placehold.co/36x36')

// 计算属性
const filteredChats = computed(() => {
  if (!searchQuery.value) return chats
  return chats.filter(chat =>
    chat.title.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
    chat.preview.toLowerCase().includes(searchQuery.value.toLowerCase())
  )
})

type ChatCompletionResponse = {
    choices:{
      index: number;
      message:{
        role: string;
        content: string;
        reasoning_content: string;
      };
      finish_reason: string;
    }[];
};
type ImageCompletionResponse = {
  data: {
    url: string;
  }[];
};
type GenerateVideoResponse = {
      requestId: string;
};
type VideoCompletionResponse = {
  status: 'InQueue' | 'Processing' | 'Succeed' | 'Failed';  // 后端返回的是 Succeed 而不是 Completed
  reason: string;
  results: {
    videos: { url: string }[];   // 数组，包含 url 对象
    timings: {
      inference: number;
    };
    seed: number;
  } | null;
};
type LessonPlanResponse = {
  content: string;
};
type AudioCompletionResponse = {
  data: string; // base64编码的音频数据
};

interface ChatItem {
  id: number;
  title: string;
  time: string;
  preview: string;
  isActive?: boolean;
}

interface MessageItem {
  id: number;
  text: string;
  time: string;
  isUser: boolean;
  isStreaming?: boolean;
  htmlContent?: string;
  questionData?: any; // 用于存储习题数据
  imageUrl?: string;
  videoUrl?: string;
  isDocument?: boolean;
  documentUrl?: string;
  documentName?: string;
  audioUrl?: string;
}

// 加载聊天历史
const loadChatHistory = async () => {
  try {
    if (!currentUserId.value) return
    
    const response = await getChatSessionsByUserId(currentUserId.value)
    if (response.data && Array.isArray(response.data)) {
      // 清空现有聊天列表
      chats.splice(0, chats.length)
      
      // 添加新的聊天记录
      response.data.forEach((session: any) => {
        chats.push({
          id: session.chatId,
          title: session.summary || '新对话',
          time: formatTime(session.createdAt),
          preview: session.summary || '开始新对话...',
          isActive: false,
        })
      })
      
      // 如果没有聊天记录，重置消息为欢迎消息
      if (chats.length === 0) {
        messages.splice(0, messages.length)
        messages.push({
          id: 1,
          text: '你好！我是你的AI助手，有什么可以帮助你的吗？',
          time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
          isUser: false
        })
      }
    }
  } catch (error) {
    console.error('加载聊天历史失败:', error)
  }
}

// 格式化时间
const formatTime = (dateString: string) => {
  const date = new Date(dateString)
  const now = new Date()
  const diffTime = now.getTime() - date.getTime()
  const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24))
  
  if (diffDays === 0) {
    return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
  } else if (diffDays === 1) {
    return '昨天'
  } else if (diffDays < 7) {
    return `${diffDays}天前`
  } else {
    return date.toLocaleDateString()
  }
}

// 加载特定会话的消息
const loadChatMessages = async (chatId: number) => {
  try {
    const response = await getChatMessagesByChatId(chatId)
    if (response.data && Array.isArray(response.data)) {
      // 清空当前消息
      messages.splice(0, messages.length)
      
      // 添加历史消息
      response.data.forEach((msg: any) => {
        const messageItem: MessageItem = {
          id: msg.messageId,
          text: msg.content,
          time: formatTime(msg.createdAt),
          isUser: msg.sender === 'user',
          isStreaming: false,
          imageUrl: msg.type === 'image' ? msg.content : undefined,
          videoUrl: msg.type === 'video' ? msg.content : undefined,
          documentUrl: msg.type === 'plan' ? msg.content : undefined,
          audioUrl: msg.type === 'audio' ? msg.content : undefined,
          isDocument: msg.type === 'plan',
          documentName: msg.type === 'plan' ? msg.content.split('/').pop() : undefined,
        }
        
        // 为AI消息渲染Markdown（非媒体类型）
        if (!messageItem.isUser && msg.type !== 'image' && msg.type !== 'video' && msg.type !== 'audio') {
          messageItem.htmlContent = renderMarkdown(messageItem.text)
        }


        messages.push(messageItem)
      })
      
      // 滚动到底部
      await nextTick()
      scrollToBottom()
    }
  } catch (error) {
    console.error('加载消息失败:', error)
  }
}

// 选择聊天
const selectChat = (chat: ChatItem) => {
  // 更新所有聊天项的活跃状态
  chats.forEach(c => c.isActive = false)
  chat.isActive = true
  
  activeChat.value = chat
  currentSessionId.value = chat.id
  isFirstMessage.value = false
  
  // 加载选中聊天的消息
  loadChatMessages(chat.id)

}

const createNewChat = () => {
  const newChat: ChatItem = {
    id: Date.now(),
    title: '新对话',
    time: '现在',
    preview: '开始新对话...',
    isActive: false
  }
  chats.unshift(newChat)
  selectChat(newChat)
  
  // 重置为新对话状态
  currentSessionId.value = null
  isFirstMessage.value = true
  
  // 清空消息并显示欢迎消息
  messages.splice(0, messages.length)
  messages.push({
    id: 1,
    text: '你好！我是你的AI助手，有什么可以帮助你的吗？',
    time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
    isUser: false
  })
}

const sendMessage = async () => {
  if (!inputMessage.value.trim() || isLoading.value) return

  const userMessage = {
    id: Date.now(),
    text: inputMessage.value,
    time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
    isUser: true
  }

  messages.push(userMessage)
  const messageText = inputMessage.value
  inputMessage.value = ''
  isLoading.value = true

  // 滚动到底部
  await nextTick()
  scrollToBottom()
  try {
    if(isFirstMessage.value ){
      if(currentUserId.value!=null){
        const chatSession = await addChatSession({
          userId:currentUserId.value ?? undefined,
          summary:messageText
      })
        currentSessionId.value=chatSession.data.chatId
        isFirstMessage.value=false
        
        // 更新当前活跃聊天的信息
        if (activeChat.value) {
          activeChat.value.id = chatSession.data.chatId
          activeChat.value.title = messageText.length > 20 ? messageText.substring(0, 20) + '...' : messageText
          activeChat.value.preview = messageText
          activeChat.value.time = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
        }
      }else{
        console.error("未获取到用户ID")
      }
    }
    try{
      const userChatMessage = await addChatMessage({
        chatId:currentSessionId.value ?? undefined,
        sender:'user',
        content:messageText,
        type:chatMode.value,
      })
    }catch(err){
      console.error('添加用户消息失败:', err)
    }
    
    // 构建包含历史上下文的消息数组
    const conversationHistory = messages
      .filter(msg => 
        msg.text !== '你好！我是你的AI助手，有什么可以帮助你的吗？' && // 排除欢迎消息
        msg.id !== Date.now() // 排除刚刚添加的用户消息（避免重复）
      )
      .map(msg => ({
        role: msg.isUser ? 'user' : 'assistant',
        content: msg.text
      }))
    
    // 添加当前消息到历史中
    conversationHistory.push({
      role: 'user',
      content: messageText
    })
    // 创建流式输出的消息占位符
    const botMessageId = Date.now() + 1
    let botMessage:MessageItem;
    if(chatMode.value === 'image') {
      botMessage = {
        id: botMessageId,
        text: '正在生成图片...',
        time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
        isUser: false,
        isStreaming: false,
        imageUrl: ''
      }
    }else if(chatMode.value === 'video') {
      botMessage = {
        id: botMessageId,
        text: '正在生成视频...',
        time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
        isUser: false,
        isStreaming: false,
        videoUrl: ''
      }
    }else if(chatMode.value === 'audio') {
      botMessage = {
        id: botMessageId,
        text: '正在生成语音...',
        time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
        isUser: false,
        isStreaming: false,
        audioUrl: undefined // 先设置为undefined，生成完成后再设置实际值
      }
    }
    else if(chatMode.value === 'plan') {
      botMessage = {
        id: botMessageId,
        text: '正在生成教案...',
        time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
        isUser: false,
        isStreaming: false,
        htmlContent: ''
      }
    }
    else{
      botMessage = {
        id: botMessageId,
        text: '',
        time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
        isUser: false,
        isStreaming: true,
        htmlContent: ''
      }
    }

    messages.push(botMessage)

    // 如果是习题生成模式，添加系统提示
    if (chatMode.value === 'exercise') {
      conversationHistory.unshift({
        role: 'system',
        content: '你是一个专业的教育助手，专门帮助用户生成各种学科的练习题和习题。请根据用户的要求生成相应的题目，包括题目内容、答案和解析。必须严格地按照以下格式的字符串输出，{\n' +
            '  "content": "",\n' +
            '  "options": {},\n' +
            '  "answer": "",\n' +
            '  "difficulty": "",\n' +
            '  "type": \n' +
            '},content是习题内容,options是选项，如果是简答题的话就是空，answer是答案，difficulty是难度，分为简单、中等、困难三种，type1表示选择，2表示填空，3表示简答题'
      })
    } else if (chatMode.value === 'audio') {
      // 为语音生成模式添加系统提示
      conversationHistory.unshift({
        role: 'system',
        content: '你是一个专业的语音内容生成助手。请根据用户的要求生成适合语音朗读的内容，包括英语听力材料、中文朗读文本等。内容应该发音清晰、语速适中、适合语音合成。如果用户要求英语内容，请生成标准的英语文本；如果要求中文内容，请生成规范的普通话文本。'
      })
    }else if(chatMode.value === 'image'){
      conversationHistory.unshift({
        role: 'system',
        content: '你是一个专业的教育图片助手，根据用户的输入生成对应的图片。'
      })
    }else if(chatMode.value === 'video'){
      conversationHistory.unshift({
        role: 'system',
        content: '你是一个专业的教育领域视频生成助手，根据用户的输入生成对应的视频。'
      })
    }else if(chatMode.value === 'plan'){
      conversationHistory.unshift({
        role: 'system',
        content: '你是一个专业的教案生成助手，请根据用户要求生成完整的教案文档。请严格按照以下格式生成教案内容：\n\n# 教案标题\n\n## 一、教学目标\n### 1. 知识目标\n- 具体的知识点1\n- 具体的知识点2\n\n### 2. 能力目标\n- 具体的能力培养目标\n\n### 3. 情感目标\n- 具体的情感态度价值观目标\n\n## 二、教学重点与难点\n### 教学重点\n- 重点内容1\n- 重点内容2\n\n### 教学难点\n- 难点内容1\n- 难点内容2\n\n## 三、教学方法\n- 教学方法和策略\n\n## 四、教学过程\n### 1. 导入环节（约X分钟）\n具体的导入活动内容\n\n### 2. 新课讲授（约X分钟）\n详细的教学内容和步骤\n\n### 3. 巩固练习（约X分钟）\n练习内容和方式\n\n### 4. 课堂小结（约X分钟）\n总结要点\n\n### 5. 作业布置\n具体的作业内容\n\n## 五、板书设计\n板书内容布局和要点\n\n## 六、教学反思\n预期的教学效果和改进建议\n\n请确保内容详实、结构清晰、符合教学规范。如果涉及数学公式，请使用LaTeX格式，如：$x^2 + y^2 = z^2$或$$\\frac{a}{b} = \\frac{c}{d}$$'
      })
    }
    
    // 打印上下文以便调试
    console.log('发送给AI的对话历史:', conversationHistory)

    
    // 滚动到底部
    await nextTick()
    scrollToBottom()

    try {
      if(chatMode.value === 'image') {
        const res = await generateImageAPI(messageText);
        const originalImageUrl = (res as ImageCompletionResponse).data[0].url;
        botMessage.imageUrl = originalImageUrl;
        
        console.log('原始图片URL:', originalImageUrl);
        
        // 验证URL格式
        try {
          new URL(originalImageUrl);
          console.log('URL格式验证通过');
        } catch (urlError) {
          console.error('URL格式无效:', urlError);
          throw new Error('生成的图片URL格式无效');
        }
        
        try {
          // 直接使用原始URL，不进行额外编码
          let UploadImageUrl = await uploadFileByUrlAPI(originalImageUrl, "chat-images");

          // 如果第一种方法失败，尝试使用编码方式
          if (UploadImageUrl.code !== 200) {
            console.log('第一种上传方法失败，尝试编码方式:', UploadImageUrl);
            UploadImageUrl = await uploadFileByUrlWithEncodingAPI(originalImageUrl, "chat-images");
          }

          console.log('图片上传响应:', UploadImageUrl);

          if (UploadImageUrl.code === 200 && UploadImageUrl.data) {
            const uploadedUrl = UploadImageUrl.data.fileUrl as string || UploadImageUrl.data.url as string;
            if (uploadedUrl) {
              botMessage.imageUrl = uploadedUrl;
              botMessage.text = "图片生成成功并已保存";

              // 保存上传后的URL到数据库
              await addChatMessage({
                chatId: currentSessionId.value ?? undefined,
                sender: 'assistant',
                content: uploadedUrl,
                type: chatMode.value,
              });

              // 保存AI资源到数据库
              try {
                await uploadAiResourceAPI({
                  fileType: '图片',
                  chatID: currentSessionId.value!,
                  userID: currentUserId.value!,
                  fileUrl: uploadedUrl,
                  title: messageText.length > 50 ? messageText.substring(0, 50) + '...' : messageText
                });
                console.log('图片资源已保存到AI资源库');
              } catch (resourceError) {
                console.error('保存图片资源失败:', resourceError);
              }
            } else {
              throw new Error('上传响应中未找到文件URL');
            }
          } else {
            throw new Error(UploadImageUrl.message || '上传失败');
          }
        } catch (uploadError) {
          console.error('图片上传失败，使用原始URL:', uploadError);
          botMessage.text = "图片生成成功";

          // 保存原始URL到数据库
          await addChatMessage({
            chatId: currentSessionId.value ?? undefined,
            sender: 'assistant',
            content: originalImageUrl,
            type: chatMode.value,
          });

          // 尝试保存原始URL到AI资源（如果聊天ID和用户ID存在）
          if (currentSessionId.value && currentUserId.value) {
            try {
              await uploadAiResourceAPI({
                fileType: '图片',
                chatID: currentSessionId.value,
                userID: currentUserId.value,
                fileUrl: originalImageUrl,
                title: messageText.length > 50 ? messageText.substring(0, 50) + '...' : messageText
              });
              console.log('图片资源（原始URL）已保存到AI资源库');
            } catch (resourceError) {
              console.error('保存图片资源（原始URL）失败:', resourceError);
            }
          }
        }
      }else if(chatMode.value === 'video') {
        const generateRes = await generateVideoAPI(messageText) as GenerateVideoResponse;
        const requestId = generateRes.requestId;
        let retries = 0;
        const maxRetries = 60; // 最大重试次数
        const retryInterval = 60000; //

        while (retries < maxRetries) {
          const statusRes = await getVideoStatusAPI(requestId) as VideoCompletionResponse;

          if (statusRes.status === 'Succeed') {
            if (statusRes.results?.videos) {
              botMessage.videoUrl = statusRes.results?.videos[0]?.url
              botMessage.text = '视频生成成功';
            } else {
              botMessage.text = '视频生成成功，但未获取到视频链接';
            }
            break;
          } else if (statusRes.status === 'Failed') {
            botMessage.text = `视频生成失败：${statusRes.reason || '未知原因'}`;
            break;
          } else {
            // 每次状态更新都告诉用户
            botMessage.text = statusRes.status === 'InQueue'
                ? `视频排队中，请耐心等待…`
                : '视频正在生成中，请耐心等待…';
          }
          retries++;
          await new Promise(resolve => setTimeout(resolve, retryInterval));
        }
        if (retries >= maxRetries) {
          botMessage.text = '视频生成超时，请稍后再试';
        }

        // 保存聊天消息
        await addChatMessage({
          chatId: currentSessionId.value ?? undefined,
          sender: 'assistant',
          content: botMessage.text,
          type: chatMode.value,
        });

        // 保存AI资源到数据库
        if (botMessage.videoUrl && currentSessionId.value && currentUserId.value) {
          try {
            await uploadAiResourceAPI({
              fileType: '视频',
              chatID: currentSessionId.value,
              userID: currentUserId.value,
              fileUrl: botMessage.videoUrl,
              title: messageText.length > 50 ? messageText.substring(0, 50) + '...' : messageText
            });
            console.log('视频资源已保存到AI资源库');
          } catch (resourceError) {
            console.error('保存视频资源失败:', resourceError);
          }
        }
      }else if(chatMode.value === 'audio') {
        try {
          const res = await textToSpeechAPI(messageText);
          console.log('语音生成响应:', res);
          console.log('响应类型:', typeof res);
          console.log('响应构造函数:', res?.constructor?.name);

          // 检查响应格式 - API返回ArrayBuffer格式的音频数据
          if (res) {
            let audioUrl: string;

            if (res instanceof ArrayBuffer) {
              // 如果是ArrayBuffer格式（API直接返回二进制音频数据）
              console.log('处理ArrayBuffer，大小:', res.byteLength);

              // 检测音频格式
              const uint8Array = new Uint8Array(res);
              const header = Array.from(uint8Array.slice(0, 12)).map(b => b.toString(16).padStart(2, '0')).join('');
              console.log('音频文件头:', header);

              let mimeType = 'audio/wav'; // 默认
              let fileExtension = 'wav';
              if (header.startsWith('494433')) { // ID3 tag, likely MP3
                mimeType = 'audio/mpeg';
                fileExtension = 'mp3';
              } else if (header.startsWith('52494646')) { // RIFF header, likely WAV
                mimeType = 'audio/wav';
                fileExtension = 'wav';
              } else if (header.startsWith('4f676753')) { // OggS header
                mimeType = 'audio/ogg';
                fileExtension = 'ogg';
              } else if (header.startsWith('664c6143')) { // fLaC header
                mimeType = 'audio/flac';
                fileExtension = 'flac';
              }

              console.log('检测到的音频类型:', mimeType);

              // 创建Blob和File对象用于上传
              const audioBlob = new Blob([res], { type: mimeType });
              const fileName = `audio_${Date.now()}.${fileExtension}`;
              const audioFile = new File([audioBlob], fileName, { type: mimeType });

              console.log('创建的音频文件:', audioFile);

              // 先创建本地播放URL
              const localAudioUrl = URL.createObjectURL(audioBlob);
              botMessage.audioUrl = localAudioUrl;
              botMessage.text = '正在上传音频到服务器...';

              // 强制更新UI显示本地音频
              const messageIndex = messages.findIndex(m => m.id === botMessage.id);
              if (messageIndex !== -1) {
                messages[messageIndex] = { ...messages[messageIndex], audioUrl: localAudioUrl, text: '正在上传音频到服务器...' };
              }

              // 上传到OSS服务器
              try {
                const uploadResult = await uploadFileAPI({
                  folder: 'chat-audios',
                  file: audioFile
                });

                console.log('音频上传结果:', uploadResult);

                if (uploadResult.code === 200 && uploadResult.data) {
                  // 获取OSS服务器返回的音频URL
                  const ossAudioUrl = uploadResult.data.fileUrl as string || uploadResult.data.url as string;

                  if (ossAudioUrl) {
                    // 更新为OSS URL
                    botMessage.audioUrl = ossAudioUrl;
                    botMessage.text = '语音生成成功并已保存到服务器';

                    // 强制更新UI显示OSS音频
                    if (messageIndex !== -1) {
                      messages[messageIndex] = { ...messages[messageIndex], audioUrl: ossAudioUrl, text: '语音生成成功并已保存到服务器' };
                    }

                    // 保存OSS URL到数据库
                    await addChatMessage({
                      chatId: currentSessionId.value ?? undefined,
                      sender: 'assistant',
                      content: ossAudioUrl,
                      type: chatMode.value,
                    });

                    // 保存AI资源到数据库
                    if (currentSessionId.value && currentUserId.value) {
                      try {
                        await uploadAiResourceAPI({
                          fileType: '语音',
                          chatID: currentSessionId.value,
                          userID: currentUserId.value,
                          fileUrl: ossAudioUrl,
                          title: messageText.length > 50 ? messageText.substring(0, 50) + '...' : messageText
                        });
                        console.log('音频资源已保存到AI资源库');
                      } catch (resourceError) {
                        console.error('保存音频资源失败:', resourceError);
                      }
                    }
                    
                    console.log(`音频上传成功，OSS URL: ${ossAudioUrl}`);
                    
                    // 释放本地Blob URL
                    URL.revokeObjectURL(localAudioUrl);
                  } else {
                    throw new Error('上传响应中未找到音频URL');
                  }
                } else {
                  throw new Error(uploadResult.message || '音频上传失败');
                }
              } catch (uploadError) {
                console.error('音频上传失败:', uploadError);
                
                // 上传失败时保持本地播放功能
                botMessage.text = '语音生成成功（上传失败，仅支持本地播放）';
                if (messageIndex !== -1) {
                  messages[messageIndex] = { ...messages[messageIndex], text: '语音生成成功（上传失败，仅支持本地播放）' };
                }
                
                // 保存失败记录到数据库
                await addChatMessage({
                  chatId: currentSessionId.value ?? undefined,
                  sender: 'assistant',
                  content: `音频生成成功 - 文件大小: ${(res.byteLength / 1024).toFixed(2)} KB (上传失败)`,
                  type: chatMode.value,
                });
              }
              
              // 强制Vue重新渲染
              await nextTick();
              scrollToBottom();
              
              // 添加使用提示
              const infoMessage: MessageItem = {
                id: Date.now() + 1,
                text: '💡 提示：音频文件已生成，您可以点击播放器播放或右键下载保存到本地。',
                time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
                isUser: false,
                isStreaming: false,
                htmlContent: '<div style="color: #3B82F6; font-weight: 500; font-size: 13px;">💡 提示：音频文件已生成，您可以点击播放器播放或右键下载保存到本地。</div>'
              };
              
              // 延迟添加提示消息，确保音频消息先显示
              setTimeout(() => {
                messages.push(infoMessage);
                nextTick(() => scrollToBottom());
              }, 500);
            } else if (res instanceof Blob) {
              // 如果是Blob格式，直接创建URL
              audioUrl = URL.createObjectURL(res);
              
              botMessage.audioUrl = audioUrl;
              botMessage.text = '语音生成成功';
              
              // 强制重新渲染
              await nextTick();
              scrollToBottom();

              // 将Blob转换为base64保存到数据库
              const reader = new FileReader();
              reader.onloadend = async () => {
                try {
                  const base64data = reader.result as string;
                  await addChatMessage({
                    chatId: currentSessionId.value ?? undefined,
                    sender: 'assistant',
                    content: base64data,
                    type: chatMode.value,
                  });
                  
                  // 保存AI资源到数据库（使用base64数据）
                  if (currentSessionId.value && currentUserId.value) {
                    try {
                      await uploadAiResourceAPI({
                        fileType: '语音',
                        chatID: currentSessionId.value,
                        userID: currentUserId.value,
                        fileUrl: base64data,
                        title: messageText.length > 50 ? messageText.substring(0, 50) + '...' : messageText
                      });
                      console.log('音频资源（Blob）已保存到AI资源库');
                    } catch (resourceError) {
                      console.error('保存音频资源（Blob）失败:', resourceError);
                    }
                  }
                } catch (saveError) {
                  console.error('保存Blob数据失败:', saveError);
                }
              };
              reader.readAsDataURL(res);
            } else if (typeof res === 'string') {
              // 判断数据类型并相应处理
              // 检查是否为base64格式
              if (res.startsWith('data:audio/') || /^[A-Za-z0-9+/]*={0,2}$/.test(res.replace(/^data:audio\/[a-z]+;base64,/, ''))) {
                try {
                  // 如果是base64字符串，转换为Blob
                  const audioBlob = base64ToBlob(res, 'audio/mpeg');
                  audioUrl = URL.createObjectURL(audioBlob);
                  
                  botMessage.audioUrl = audioUrl;
                  botMessage.text = '语音生成成功';
                  
                  // 保存base64数据到数据库
                  await addChatMessage({
                    chatId: currentSessionId.value ?? undefined,
                    sender: 'assistant',
                    content: res,
                    type: chatMode.value,
                  });
                  
                  // 保存AI资源到数据库
                  if (currentSessionId.value && currentUserId.value) {
                    try {
                      await uploadAiResourceAPI({
                        fileType: '语音',
                        chatID: currentSessionId.value,
                        userID: currentUserId.value,
                        fileUrl: res,
                        title: messageText.length > 50 ? messageText.substring(0, 50) + '...' : messageText
                      });
                      console.log('音频资源（base64）已保存到AI资源库');
                    } catch (resourceError) {
                      console.error('保存音频资源（base64）失败:', resourceError);
                    }
                  }
                } catch (base64Error) {
                  console.error('Base64处理失败，尝试作为URL处理:', base64Error);
                  // 如果base64处理失败，尝试作为URL处理
                  botMessage.audioUrl = res;
                  botMessage.text = '语音生成成功';

                  await addChatMessage({
                    chatId: currentSessionId.value ?? undefined,
                    sender: 'assistant',
                    content: res,
                    type: chatMode.value,
                  });
                }
              } else {
                // 如果不是base64格式，尝试作为URL处理
                console.log('处理为URL格式');
                botMessage.audioUrl = res;
                botMessage.text = '语音生成成功';

                await addChatMessage({
                  chatId: currentSessionId.value ?? undefined,
                  sender: 'assistant',
                  content: res,
                  type: chatMode.value,
                });

                // 保存AI资源到数据库
                if (currentSessionId.value && currentUserId.value) {
                  try {
                    await uploadAiResourceAPI({
                      fileType: '语音',
                      chatID: currentSessionId.value,
                      userID: currentUserId.value,
                      fileUrl: res,
                      title: messageText.length > 50 ? messageText.substring(0, 50) + '...' : messageText
                    });
                    console.log('音频资源（URL）已保存到AI资源库');
                  } catch (resourceError) {
                    console.error('保存音频资源（URL）失败:', resourceError);
                  }
                }
              }
            } else if (res && typeof res === 'object' && 'data' in res) {
              // 如果响应是包含data字段的对象
              const audioData = (res as any).data;
              if (audioData instanceof ArrayBuffer) {
                const audioBlob = new Blob([audioData], { type: 'audio/mpeg' });
                audioUrl = URL.createObjectURL(audioBlob);
                
                botMessage.audioUrl = audioUrl;
                botMessage.text = '语音生成成功';
                
                // 避免保存大文件到数据库
                try {
                  await addChatMessage({
                    chatId: currentSessionId.value ?? undefined,
                    sender: 'assistant',
                    content: `音频生成成功 - 文件大小: ${(audioData.byteLength / 1024).toFixed(2)} KB`,
                    type: chatMode.value,
                  });
                } catch (saveError) {
                  console.error('保存音频记录失败:', saveError);
                }
              } else if (typeof audioData === 'string') {
                try {
                  const audioBlob = base64ToBlob(audioData, 'audio/mpeg');
                  audioUrl = URL.createObjectURL(audioBlob);
                  
                  botMessage.audioUrl = audioUrl;
                  botMessage.text = '语音生成成功';

                  await addChatMessage({
                    chatId: currentSessionId.value ?? undefined,
                    sender: 'assistant',
                    content: audioData,
                    type: chatMode.value,
                  });
                } catch (parseError) {
                  console.error('解析嵌套音频数据失败:', parseError);
                  throw new Error('音频数据格式无效');
                }
              }
            } else {
              // 其他情况，尝试转换为字符串后处理
              console.log('未知的音频数据格式，尝试转换为字符串处理');
              const stringData = String(res);
              botMessage.audioUrl = stringData;
              botMessage.text = '语音生成成功';
              
              await addChatMessage({
                chatId: currentSessionId.value ?? undefined,
                sender: 'assistant',
                content: stringData,
                type: chatMode.value,
              });
            }
          } else {
            throw new Error('音频生成失败，未获取到音频数据');
          }
        } catch (audioError: any) {
          console.error('语音生成失败:', audioError);
          botMessage.text = `抱歉，语音生成失败：${audioError?.message || '未知错误'}`;
          
          await addChatMessage({
            chatId: currentSessionId.value ?? undefined,
            sender: 'assistant',
            content: '语音生成失败',
            type: chatMode.value,
          });
        }
      }
      else{
        const res = await getChatCompletionAPI(
            conversationHistory,
            chatMode.value === 'exercise' ? 'json_object' : undefined
        );
        const botMessageContent = (res as ChatCompletionResponse).choices[0].message.content

        let displayContent = botMessageContent;
        let questionData = null;
        if(chatMode.value === 'exercise') {
          try {
            // 解析AI生成的JSON字符串
            questionData = JSON.parse(botMessageContent);

            // 格式化习题内容用于显示
            displayContent = formatQuestionForDisplay(questionData);

            // 将习题数据存储到消息中，用于后续保存
            botMessage.questionData = questionData;

          } catch (error) {
            console.error('解析习题JSON失败:', error);
            displayContent = '抱歉，生成的习题格式有误，请重新尝试。';
          }
          await simulateStreamingOutput(botMessageId, displayContent)

          // 保存消息到数据库
          const botChatMessage = await addChatMessage({
            chatId: currentSessionId.value ?? undefined,
            sender: 'assistant',
            content: displayContent, // 使用格式化后的内容保存
            type: chatMode.value,
          })
        }else if(chatMode.value === 'plan') {
          try {
            // 生成文档
            const doc = new Document({
              creator: "AI助手",
              title: "教案文档",
              description: "AI生成的教案文档",
            sections: [
              {
                properties: {
                  page: {
                    margin: {
                      top: 1440,    // 1英寸 = 1440 twips
                      right: 1440,
                      bottom: 1440,
                      left: 1440,
                    },
                  },
                },
                children: [
                  // 解析 Markdown 内容并添加格式
                  ...parseMarkdownToDocx(botMessageContent)
                ],
              },
            ],
          });
          const blob = await Packer.toBlob(doc);
          const documentName = `lesson_plan_${new Date().getTime()}.docx`;
          const localDocumentUrl = window.URL.createObjectURL(blob);
          botMessage.isDocument = true;
          botMessage.documentUrl = localDocumentUrl;
          botMessage.documentName = documentName;
          
          // 添加文档预览内容（Markdown格式的原始内容）
          botMessage.htmlContent = renderMarkdown(botMessageContent);
          botMessage.text = '教案文档已生成。以下是预览内容：';

          // 先显示预览内容
          await simulateStreamingOutput(botMessageId, botMessage.text);
          
          // 然后更新为包含下载链接的完整内容
          setTimeout(() => {
            botMessage.text = '教案文档已生成，可预览内容并点击下载Word文档';
          }, 1000);

          // 上传文档到云端
          const file = new File([blob], documentName, { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
          try {
            // 上传文档到 OSS
            const uploadResult = await uploadFileAPI({
              folder: 'chat-documents',
              file: file
            });

            if (uploadResult.code === 200 && uploadResult.data) {
              const ossDocumentUrl = uploadResult.data.fileUrl as string || uploadResult.data.url as string;
              if (ossDocumentUrl) {
                // 更新文档 URL 为 OSS 地址
                botMessage.documentUrl = ossDocumentUrl;
                botMessage.text = '教案文档已生成，点击下载';

                // 保存 OSS URL 到数据库
                await addChatMessage({
                  chatId: currentSessionId.value ?? undefined,
                  sender: 'assistant',
                  content: ossDocumentUrl,
                  type: chatMode.value,
                });

                // 保存 AI 资源到数据库
                if (currentSessionId.value && currentUserId.value) {
                  try {
                    await uploadAiResourceAPI({
                      fileType: '文档',
                      chatID: currentSessionId.value,
                      userID: currentUserId.value,
                      fileUrl: ossDocumentUrl,
                      title: messageText.length > 50 ? messageText.substring(0, 50) + '...' : messageText
                    });
                    console.log('教案文档资源已保存到 AI 资源库');
                  } catch (resourceError) {
                    console.error('保存教案文档资源失败:', resourceError);
                  }
                }
              } else {
                console.error('上传响应中未找到文件 URL');
                botMessage.text = '教案文档生成成功，但上传失败，点击下载本地文件';
              }
            } else {
              console.error(uploadResult.message || '文档上传失败');
              botMessage.text = '教案文档生成成功，但上传失败，点击下载本地文件';
            }
          } catch (uploadError) {
            console.error('文档上传失败:', uploadError);
            botMessage.text = '教案文档生成成功，但上传失败，点击下载本地文件';
          }
          } catch (docError) {
            console.error('文档生成失败:', docError);
            // 如果Word文档生成失败，提供HTML版本
            botMessage.htmlContent = renderMarkdown(botMessageContent);
            botMessage.text = '教案内容已生成（Word文档生成失败，但您可以查看以下预览内容）：';
            botMessage.isDocument = false;
            
            // 保存原始内容到数据库
            await addChatMessage({
              chatId: currentSessionId.value ?? undefined,
              sender: 'assistant',
              content: botMessageContent,
              type: chatMode.value,
            });
          }
        } else {
          // 普通对话模式
          await simulateStreamingOutput(botMessageId, displayContent);
          
          // 保存消息到数据库
          await addChatMessage({
            chatId: currentSessionId.value ?? undefined,
            sender: 'assistant',
            content: displayContent,
            type: chatMode.value,
          });
        }
      }
    } catch (error: any) {
      console.error('获取AI回复失败:', error)
      updateStreamingMessage(botMessageId, '抱歉，获取回复时出错，请稍后再试。')
      finishStreaming(botMessageId)
    }
  }catch(err) {
    console.error('Error sending message:', err)
    // 如果没有创建流式消息，创建错误消息
    if (messages.findIndex(msg => msg.id === Date.now() + 1) === -1) {
      const errMessage: MessageItem = {
        id: Date.now() + 1,
        text: '抱歉，获取回复时出错，请稍后再试。',
        time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
        isUser: false,
        isStreaming: false,
        htmlContent: '抱歉，获取回复时出错，请稍后再试。'
      }
      messages.push(errMessage)
    }
  }finally {
    isLoading.value = false
    await nextTick(() => scrollToBottom())
  }
}

const setChatMode = (mode: 'normal' | 'exercise'| 'image' | 'video'| 'plan'|'audio') => {
  chatMode.value = mode
}

const scrollToBottom = () => {
  if (messagesContainer.value) {
    messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
  }
}

const toggleSettings = () => {
  console.log('Toggle settings')
}

const goToProfile = () => {
  router.push('/personal-info')
}


// 渲染Markdown为HTML
const renderMarkdown = (text: string): string => {
  try {
    return marked.parse(text) as string
  } catch (error) {
    console.error('Markdown渲染错误:', error)
    return text
  }
}

const parseMarkdownToDocx = (markdown: string) => {
  const lines = markdown.split('\n');
  const paragraphs: any[] = [];
  
  // 定义编号引用
  const numberingRefs = {
    bullet: 'bullet-list',
    ordered: 'ordered-list'
  };

  lines.forEach((line, index) => {
    // 跳过空行，但添加段落间距
    if (line.trim() === '') {
      if (index > 0 && index < lines.length - 1) {
        paragraphs.push(
          new Paragraph({
            text: '',
            spacing: {
              after: 120
            }
          })
        );
      }
      return;
    }

    // 处理LaTeX公式 $...$ 和 $$...$$
    if (line.includes('$')) {
      const processedLine = line.replace(/\$\$(.*?)\$\$/g, '【公式：$1】')
                               .replace(/\$(.*?)\$/g, '【公式：$1】');
      line = processedLine;
    }

    if (line.startsWith('# ')) {
      // 一级标题
      paragraphs.push(
        new Paragraph({
          text: line.replace('# ', ''),
          heading: 'Heading1',
          spacing: {
            before: 400,
            after: 200
          },
          alignment: 'center'
        })
      );
    } else if (line.startsWith('## ')) {
      // 二级标题
      paragraphs.push(
        new Paragraph({
          text: line.replace('## ', ''),
          heading: 'Heading2',
          spacing: {
            before: 300,
            after: 150
          }
        })
      );
    } else if (line.startsWith('### ')) {
      // 三级标题
      paragraphs.push(
        new Paragraph({
          text: line.replace('### ', ''),
          heading: 'Heading3',
          spacing: {
            before: 200,
            after: 120
          }
        })
      );
    } else if (line.startsWith('#### ')) {
      // 四级标题
      paragraphs.push(
        new Paragraph({
          text: line.replace('#### ', ''),
          heading: 'Heading4',
          spacing: {
            before: 160,
            after: 100
          }
        })
      );
    } else if (line.startsWith('* ') || line.startsWith('- ')) {
      // 无序列表
      paragraphs.push(
        new Paragraph({
          text: line.replace(/^\* |^- /, ''),
          bullet: {
            level: 0
          },
          spacing: {
            after: 80
          }
        })
      );
    } else if (/^\d+\. /.test(line)) {
      // 有序列表
      paragraphs.push(
        new Paragraph({
          text: line.replace(/^\d+\. /, ''),
          numbering: {
            reference: 'ordered-numbering',
            level: 0
          },
          spacing: {
            after: 80
          }
        })
      );
    } else if (line.startsWith('  * ') || line.startsWith('  - ')) {
      // 二级无序列表
      paragraphs.push(
        new Paragraph({
          text: line.replace(/^  \* |^  - /, ''),
          bullet: {
            level: 1
          },
          spacing: {
            after: 60
          }
        })
      );
    } else if (/^  \d+\. /.test(line)) {
      // 二级有序列表
      paragraphs.push(
        new Paragraph({
          text: line.replace(/^  \d+\. /, ''),
          numbering: {
            reference: 'ordered-numbering',
            level: 1
          },
          spacing: {
            after: 60
          }
        })
      );
    } else if (line.startsWith('**') && line.endsWith('**')) {
      // 粗体段落
      paragraphs.push(
        new Paragraph({
          children: [
            new TextRun({
              text: line.replace(/^\*\*|\*\*$/g, ''),
              bold: true
            })
          ],
          spacing: {
            after: 120
          }
        })
      );
    } else if (line.startsWith('*') && line.endsWith('*')) {
      // 斜体段落
      paragraphs.push(
        new Paragraph({
          children: [
            new TextRun({
              text: line.replace(/^\*|\*$/g, ''),
              italics: true
            })
          ],
          spacing: {
            after: 120
          }
        })
      );
    } else {
      // 普通段落，处理内联格式
      const children: any[] = [];
      
      // 简单的内联格式处理
      if (line.includes('**') || line.includes('*') || line.includes('【公式：')) {
        const parts = line.split(/(\*\*.*?\*\*|\*.*?\*|【公式：.*?】)/);
        parts.forEach(part => {
          if (part.startsWith('**') && part.endsWith('**')) {
            children.push(new TextRun({
              text: part.replace(/^\*\*|\*\*$/g, ''),
              bold: true
            }));
          } else if (part.startsWith('*') && part.endsWith('*')) {
            children.push(new TextRun({
              text: part.replace(/^\*|\*$/g, ''),
              italics: true
            }));
          } else if (part.startsWith('【公式：') && part.endsWith('】')) {
            children.push(new TextRun({
              text: part,
              color: '0066CC',
              italics: true
            }));
          } else if (part) {
            children.push(new TextRun({
              text: part
            }));
          }
        });
      } else {
        children.push(new TextRun({
          text: line
        }));
      }

      paragraphs.push(
        new Paragraph({
          children: children,
          spacing: {
            after: 120
          },
          indent: {
            firstLine: 420 // 首行缩进2字符 (约420 twips)
          }
        })
      );
    }
  });

  return paragraphs;
};


// base64转换为Blob的辅助函数
const base64ToBlob = (base64: string, mimeType: string = 'audio/mpeg'): Blob => {
  try {
    // 移除base64前缀（如果存在）
    const base64Data = base64.replace(/^data:audio\/[a-z]+;base64,/, '');

    // 验证是否为有效的base64字符串
    if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64Data)) {
      throw new Error('Invalid base64 string format');
    }

    // 解码base64
    const byteCharacters = atob(base64Data);
    const byteNumbers = new Array(byteCharacters.length);

    for (let i = 0; i < byteCharacters.length; i++) {
      byteNumbers[i] = byteCharacters.charCodeAt(i);
    }

    const byteArray = new Uint8Array(byteNumbers);
    return new Blob([byteArray], { type: mimeType });
  } catch (error) {
    console.error('Base64解码失败:', error);
    // 如果base64解码失败，尝试直接将字符串作为文本处理
    const encoder = new TextEncoder();
    const uint8Array = encoder.encode(base64);
    return new Blob([uint8Array], { type: 'text/plain' });
  }
}

// 格式化习题用于显示
const formatQuestionForDisplay = (questionData: any): string => {
  let formattedContent = `**题目：**\n${questionData.content}\n\n`;

  // 根据题目类型格式化选项
  if (questionData.type === 0 && questionData.options) { // 选择题
    formattedContent += "**选项：**\n";
    Object.entries(questionData.options).forEach(([key, value]) => {
      formattedContent += `${key}. ${value}\n`;
    });
    formattedContent += "\n";
  } else if (questionData.type === 1) { // 判断题
    formattedContent += "**请判断正误**\n\n";
  }

  formattedContent += `**答案：** ${questionData.answer}\n\n`;
  formattedContent += `**难度：** ${questionData.difficulty}\n\n`;

  // 添加题目类型说明
  const typeMap = { 1: '选择题', 2: '填空题', 3: '简答题' };
  formattedContent += `**题目类型：** ${typeMap[questionData.type as keyof typeof typeMap] || '未知'}\n\n`;

  return formattedContent;
}

// 保存习题到后端
const saveQuestion = async (questionData: any) => {
  try {
    // 从localStorage获取用户ID作为creatorId
    const creatorId = localStorage.getItem('id');

    // 准备addQuestionAPI的参数
    const questionParams = {
      content: questionData.content,
      options: questionData.options,
      answer: questionData.answer,
      difficulty: questionData.difficulty,
      type: questionData.type,
      creatorId: creatorId ? parseInt(creatorId) : undefined
    };

    // 调用addQuestionAPI将习题添加到后端
    const addResult = await addQuestionAPI(questionParams);

    if (addResult.code === 200) {
      console.log('习题添加成功:', addResult);

      // 添加成功提示消息
      const successMessage: MessageItem = {
        id: Date.now(),
        text: '✅ 习题已成功保存到题库！',
        time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
        isUser: false,
        isStreaming: false,
        htmlContent: '<div style="color: #22C55E; font-weight: 500;">✅ 习题已成功保存到题库！</div>'
      };

      messages.push(successMessage);

      // 滚动到底部
      await nextTick();
      scrollToBottom();

    } else {
      console.error('习题添加失败:', addResult.message);

      // 添加失败提示消息
      const errorMessage: MessageItem = {
        id: Date.now(),
        text: `❌ 习题保存失败：${addResult.message}`,
        time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
        isUser: false,
        isStreaming: false,
        htmlContent: `<div style="color: #EF4444; font-weight: 500;">❌ 习题保存失败：${addResult.message}</div>`
      };

      messages.push(errorMessage);
      await nextTick();
      scrollToBottom();
    }
  } catch (error) {
    console.error('保存习题时出错:', error);

    // 添加错误提示消息
    const errorMessage: MessageItem = {
      id: Date.now(),
      text: '❌ 保存习题时出错，请稍后重试。',
      time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
      isUser: false,
      isStreaming: false,
      htmlContent: '<div style="color: #EF4444; font-weight: 500;">❌ 保存习题时出错，请稍后重试。</div>'
    };

    messages.push(errorMessage);
    await nextTick();
    scrollToBottom();
  }
}

// 流式更新消息内容
const updateStreamingMessage = (messageId: number, newText: string) => {
  const messageIndex = messages.findIndex(msg => msg.id === messageId)
  if (messageIndex !== -1) {
    messages[messageIndex].text = newText
    // 只有当消息不是流式输出时才渲染Markdown，避免频繁渲染
    if (!messages[messageIndex].isStreaming) {
      messages[messageIndex].htmlContent = renderMarkdown(newText)
    }
  }
}

// 完成流式输出
const finishStreaming = (messageId: number) => {
  const messageIndex = messages.findIndex(msg => msg.id === messageId)
  if (messageIndex !== -1) {
    messages[messageIndex].isStreaming = false
    messages[messageIndex].htmlContent = renderMarkdown(messages[messageIndex].text)
  }
}

const downloadImage = async (imageUrl: string) => {
  try {
    // 方法一：直接使用链接下载（适用于大部分情况）
    const a = document.createElement('a');
    a.href = imageUrl;
    a.target = '_blank'; // 在新窗口打开
    a.download = `generated_image_${new Date().getTime()}.png`;
    
    // 添加到DOM并点击
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    
    // 如果直接下载失败，尝试通过代理下载
  } catch (error) {
    console.error('直接下载失败，尝试其他方法:', error);
    
    try {
      // 方法二：使用代理或者通过后端下载
      // 这里可以调用后端API来下载文件
      const proxyUrl = `/api/file/download?url=${encodeURIComponent(imageUrl)}`;
      const a = document.createElement('a');
      a.href = proxyUrl;
      a.download = `generated_image_${new Date().getTime()}.png`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    } catch (proxyError) {
      console.error('代理下载也失败:', proxyError);
      
      // 方法三：在新窗口打开图片，让用户手动保存
      try {
        window.open(imageUrl, '_blank');
        
        // 显示提示信息
        const infoMessage: MessageItem = {
          id: Date.now(),
          text: '💡 图片已在新窗口打开，您可以右键保存图片到本地。',
          time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
          isUser: false,
          isStreaming: false,
          htmlContent: '<div style="color: #3B82F6; font-weight: 500;">💡 图片已在新窗口打开，您可以右键保存图片到本地。</div>'
        };
        messages.push(infoMessage);
        await nextTick();
        scrollToBottom();
      } catch (openError) {
        console.error('所有下载方法都失败:', openError);
        
        // 添加下载失败提示消息
        const errorMessage: MessageItem = {
          id: Date.now(),
          text: '❌ 图片下载失败，请稍后重试。您也可以右键点击图片选择"图片另存为"。',
          time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
          isUser: false,
          isStreaming: false,
          htmlContent: '<div style="color: #EF4444; font-weight: 500;">❌ 图片下载失败，请稍后重试。您也可以右键点击图片选择"图片另存为"。</div>'
        };
        messages.push(errorMessage);
        await nextTick();
        scrollToBottom();
      }
    }
  }
};

const downloadVideo = async (videoUrl: string) => {
  try {
    // 方法一：直接使用链接下载（适用于大部分情况）
    const a = document.createElement('a');
    a.href = videoUrl;
    a.target = '_blank'; // 在新窗口打开
    a.download = `generated_video_${new Date().getTime()}.mp4`;
    
    // 添加到DOM并点击
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    
  } catch (error) {
    console.error('直接下载失败，尝试其他方法:', error);
    
    try {
      // 方法二：使用代理或者通过后端下载
      const proxyUrl = `/api/file/download?url=${encodeURIComponent(videoUrl)}`;
      const a = document.createElement('a');
      a.href = proxyUrl;
      a.download = `generated_video_${new Date().getTime()}.mp4`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    } catch (proxyError) {
      console.error('代理下载也失败:', proxyError);
      
      // 方法三：在新窗口打开视频，让用户手动保存
      try {
        window.open(videoUrl, '_blank');
        
        // 显示提示信息
        const infoMessage: MessageItem = {
          id: Date.now(),
          text: '💡 视频已在新窗口打开，您可以右键保存视频到本地。',
          time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
          isUser: false,
          isStreaming: false,
          htmlContent: '<div style="color: #3B82F6; font-weight: 500;">💡 视频已在新窗口打开，您可以右键保存视频到本地。</div>'
        };
        messages.push(infoMessage);
        await nextTick();
        scrollToBottom();
      } catch (openError) {
        console.error('所有下载方法都失败:', openError);
        
        // 添加下载失败提示消息
        const errorMessage: MessageItem = {
          id: Date.now(),
          text: '❌ 视频下载失败，请稍后重试。您也可以右键点击视频选择"视频另存为"。',
          time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
          isUser: false,
          isStreaming: false,
          htmlContent: '<div style="color: #EF4444; font-weight: 500;">❌ 视频下载失败，请稍后重试。您也可以右键点击视频选择"视频另存为"。</div>'
        };
        messages.push(errorMessage);
        await nextTick();
        scrollToBottom();
      }
    }
  }
};

const downloadAudio = async (audioUrl: string) => {
  try {
    // 方法一：直接使用链接下载
    const a = document.createElement('a');
    a.href = audioUrl;
    a.target = '_blank';
    a.download = `generated_audio_${new Date().getTime()}.mp3`;
    
    // 添加到DOM并点击
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    
  } catch (error) {
    console.error('直接下载失败，尝试其他方法:', error);
    
    try {
      // 方法二：使用代理下载
      const proxyUrl = `/api/file/download?url=${encodeURIComponent(audioUrl)}`;
      const a = document.createElement('a');
      a.href = proxyUrl;
      a.download = `generated_audio_${new Date().getTime()}.mp3`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    } catch (proxyError) {
      console.error('代理下载也失败:', proxyError);

      // 方法三：在新窗口打开音频
      try {
        window.open(audioUrl, '_blank');
        
        // 显示提示信息
        const infoMessage: MessageItem = {
          id: Date.now(),
          text: '💡 音频已在新窗口打开，您可以右键保存音频到本地。',
          time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
          isUser: false,
          isStreaming: false,
          htmlContent: '<div style="color: #3B82F6; font-weight: 500;">💡 音频已在新窗口打开，您可以右键保存音频到本地。</div>'
        };
        messages.push(infoMessage);
        await nextTick();
        scrollToBottom();
      } catch (openError) {
        console.error('所有下载方法都失败:', openError);
        
        // 添加下载失败提示消息
        const errorMessage: MessageItem = {
          id: Date.now(),
          text: '❌ 音频下载失败，请稍后重试。您也可以右键点击音频播放器选择"音频另存为"。',
          time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
          isUser: false,
          isStreaming: false,
          htmlContent: '<div style="color: #EF4444; font-weight: 500;">❌ 音频下载失败，请稍后重试。您也可以右键点击音频播放器选择"音频另存为"。</div>'
        };
        messages.push(errorMessage);
        await nextTick();
        scrollToBottom();
      }
    }
  }
};

const downloadLessonPlan = async (message: MessageItem) => {
  if (message.documentUrl) {
    const link = document.createElement('a');
    link.href = message.documentUrl;
    link.download = message.documentName || 'lesson_plan.docx';
    link.click();
  }
};

// 模拟流式输出效果
const simulateStreamingOutput = async (messageId: number, fullText: string) => {
  const words = fullText.split('')
  let currentText = ''
  
  for (let i = 0; i < words.length; i++) {
    currentText += words[i]
    updateStreamingMessage(messageId, currentText)
    
    // 滚动到底部
    await nextTick()
    scrollToBottom()
    
    // 控制输出速度
    await new Promise(resolve => setTimeout(resolve, 30))
  }
  
  // 完成流式输出
  finishStreaming(messageId)
  
  // 最后滚动到底部
  await nextTick()
  scrollToBottom()
}

// 开始录音
const startRecording = async () => {
  try {
    // 检查浏览器支持
    if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
      throw new Error('您的浏览器不支持录音功能')
    }

    const stream = await navigator.mediaDevices.getUserMedia({
      audio: {
        echoCancellation: true,
        noiseSuppression: true,
        sampleRate: 16000
      }
    })

    // 检查MediaRecorder支持
    if (!MediaRecorder) {
      throw new Error('您的浏览器不支持MediaRecorder')
    }

    // 选择合适的音频格式
    let options: MediaRecorderOptions = {}
    if (MediaRecorder.isTypeSupported('audio/webm;codecs=opus')) {
      options.mimeType = 'audio/webm;codecs=opus'
    } else if (MediaRecorder.isTypeSupported('audio/mp4')) {
      options.mimeType = 'audio/mp4'
    } else if (MediaRecorder.isTypeSupported('audio/wav')) {
      options.mimeType = 'audio/wav'
    }

    mediaRecorder.value = new MediaRecorder(stream, options)
    audioChunks.value = []
    recordingTime.value = 0

    mediaRecorder.value.ondataavailable = (event) => {
      if (event.data.size > 0) {
        audioChunks.value.push(event.data)
      }
    }

    mediaRecorder.value.onstop = async () => {
      const mimeType = mediaRecorder.value?.mimeType || 'audio/wav'
      const audioBlob = new Blob(audioChunks.value, { type: mimeType })

      // 根据MIME类型确定文件扩展名
      let extension = 'wav'
      if (mimeType.includes('webm')) {
        extension = 'webm'
      } else if (mimeType.includes('mp4')) {
        extension = 'm4a'
      } else if (mimeType.includes('mpeg')) {
        extension = 'mp3'
      }

      const audioFile = new File([audioBlob], `recording_${Date.now()}.${extension}`, { type: mimeType })

      console.log('录音完成，文件信息:', {
        name: audioFile.name,
        size: audioFile.size,
        type: audioFile.type
      })

      // 转换音频为文字
      await convertSpeechToText(audioFile)

      // 清理录音状态
      cleanup()
    }

    mediaRecorder.value.start()
    isRecording.value = true

    // 开始计时
    recordingTimer.value = setInterval(() => {
      recordingTime.value++

      // 限制最大录音时长（比如60秒）
      if (recordingTime.value >= 60) {
        stopRecording()

        // 添加提示消息
        const warningMessage: MessageItem = {
          id: Date.now(),
          text: '⚠️ 录音已达到最大时长（60秒），已自动停止。',
          time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
          isUser: false,
          isStreaming: false,
          htmlContent: '<div style="color: #F59E0B; font-weight: 500;">⚠️ 录音已达到最大时长（60秒），已自动停止。</div>'
        }
        messages.push(warningMessage)
        nextTick(() => scrollToBottom())
      }
    }, 1000)

    // 添加开始录音的提示
    const startMessage: MessageItem = {
      id: Date.now(),
      text: '🎤 开始录音... 请说话（最长60秒）',
      time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
      isUser: false,
      isStreaming: false,
      htmlContent: '<div style="color: #3B82F6; font-weight: 500;">🎤 开始录音... 请说话（最长60秒）</div>'
    }
    messages.push(startMessage)
    await nextTick()
    scrollToBottom()

  } catch (error: any) {
    console.error('录音失败:', error)

    let errorMessage = '录音失败'
    if (error.name === 'NotAllowedError') {
      errorMessage = '录音权限被拒绝，请允许访问麦克风权限'
    } else if (error.name === 'NotFoundError') {
      errorMessage = '未找到麦克风设备'
    } else if (error.name === 'NotSupportedError') {
      errorMessage = '您的浏览器不支持录音功能'
    } else if (error.message) {
      errorMessage = error.message
    }

    // 添加错误提示消息
    const errorMessageItem: MessageItem = {
      id: Date.now(),
      text: `❌ ${errorMessage}`,
      time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
      isUser: false,
      isStreaming: false,
      htmlContent: `<div style="color: #EF4444; font-weight: 500;">❌ ${errorMessage}</div>`
    }
    messages.push(errorMessageItem)
    await nextTick()
    scrollToBottom()
  }
}

// 停止录音
const stopRecording = () => {
  if (mediaRecorder.value && isRecording.value) {
    mediaRecorder.value.stop()
    mediaRecorder.value.stream.getTracks().forEach(track => track.stop())

    // 添加停止录音的提示
    const stopMessage: MessageItem = {
      id: Date.now(),
      text: `🎤 录音已停止（时长：${formatRecordingTime(recordingTime.value)}）`,
      time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
      isUser: false,
      isStreaming: false,
      htmlContent: `<div style="color: #3B82F6; font-weight: 500;">🎤 录音已停止（时长：${formatRecordingTime(recordingTime.value)}）</div>`
    }
    messages.push(stopMessage)
    nextTick(() => scrollToBottom())
  }
}

// 清理录音相关资源
const cleanup = () => {
  isRecording.value = false
  mediaRecorder.value = null
  audioChunks.value = []
  recordingTime.value = 0

  if (recordingTimer.value) {
    clearInterval(recordingTimer.value)
    recordingTimer.value = null
  }

  // 清理一些临时的录音状态提示消息
  const tempMessages = ['开始录音', '录音已停止']
  for (let i = messages.length - 1; i >= 0; i--) {
    const message = messages[i]
    if (!message.isUser && tempMessages.some(temp => message.text.includes(temp))) {
      // 只清理最近5分钟内的临时消息
      const messageTime = new Date(`1970/01/01 ${message.time}`)
      const now = new Date()
      const currentTime = new Date(`1970/01/01 ${now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`)
      const timeDiff = Math.abs(currentTime.getTime() - messageTime.getTime())

      if (timeDiff < 5 * 60 * 1000) { // 5分钟内
        messages.splice(i, 1)
      }
      break // 只删除最近的一条
    }
  }
}

// 转换语音为文字
const convertSpeechToText = async (audioFile: File) => {
  try {
    console.log('开始转换语音，文件大小:', audioFile.size, '字节')

    // 添加处理中的消息
    const processingMessage: MessageItem = {
      id: Date.now(),
      text: '🎤 正在转换语音为文字...',
      time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
      isUser: false,
      isStreaming: false,
      htmlContent: '<div style="color: #3B82F6; font-weight: 500;">🎤 正在转换语音为文字...</div>'
    }
    messages.push(processingMessage)
    await nextTick()
    scrollToBottom()

    // 调用语音转文字API
    const response = await speechToTextAPI(audioFile)

    console.log('语音转文字响应:', response)

    // 检查响应格式
    let transcribedText = ''
    if (response.data && response.data.text) {
      transcribedText = response.data.text
    } else if (response.data && typeof response.data === 'string') {
      // 如果响应是纯文本字符串
      transcribedText = response.data
    } else if (typeof response === 'string') {
      transcribedText = response
    } else if (response.data && response.data.transcript) {
      // 一些API可能使用transcript字段
      transcribedText = response.data.transcript
    } else {
      console.error('未知的响应格式:', response)
      throw new Error('无法解析语音转文字结果')
    }

    if (transcribedText.trim()) {
      // 移除处理中的消息
      const processingIndex = messages.findIndex(m => m.id === processingMessage.id)
      if (processingIndex !== -1) {
        messages.splice(processingIndex, 1)
      }

      // 将转换的文字填入输入框
      inputMessage.value = transcribedText.trim()

      // 添加成功提示消息
      const successMessage: MessageItem = {
        id: Date.now(),
        text: `✅ 语音转换成功: "${transcribedText.trim()}"`,
        time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
        isUser: false,
        isStreaming: false,
        htmlContent: `<div style="color: #22C55E; font-weight: 500;">✅ 语音转换成功: "${transcribedText.trim()}"</div>`
      }
      messages.push(successMessage)

    } else {
      throw new Error('语音转换结果为空')
    }

  } catch (error: any) {
    console.error('语音转文字失败:', error)

    // 移除处理中的消息
    const processingIndex = messages.findIndex(m => m.text.includes('正在转换语音为文字'))
    if (processingIndex !== -1) {
      messages.splice(processingIndex, 1)
    }

    let errorMessage = '未知错误'
    if (error?.response?.status === 401) {
      errorMessage = 'API认证失败，请检查密钥'
    } else if (error?.response?.status === 429) {
      errorMessage = '请求过于频繁，请稍后再试'
    } else if (error?.response?.status >= 500) {
      errorMessage = '服务器错误，请稍后再试'
    } else if (error?.message) {
      errorMessage = error.message
    }

    // 添加错误提示消息
    const errorMessageItem: MessageItem = {
      id: Date.now(),
      text: `❌ 语音转文字失败: ${errorMessage}`,
      time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
      isUser: false,
      isStreaming: false,
      htmlContent: `<div style="color: #EF4444; font-weight: 500;">❌ 语音转文字失败: ${errorMessage}</div>`
    }
    messages.push(errorMessageItem)
  }

  await nextTick()
  scrollToBottom()
}

// 格式化录音时间显示
const formatRecordingTime = (seconds: number): string => {
  const mins = Math.floor(seconds / 60)
  const secs = seconds % 60
  return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
}

// 测试麦克风权限
const testMicrophone = async () => {
  try {
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
    stream.getTracks().forEach(track => track.stop())

    const testMessage: MessageItem = {
      id: Date.now(),
      text: '✅ 麦克风测试成功，录音功能可正常使用！',
      time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
      isUser: false,
      isStreaming: false,
      htmlContent: '<div style="color: #22C55E; font-weight: 500;">✅ 麦克风测试成功，录音功能可正常使用！</div>'
    }
    messages.push(testMessage)
    await nextTick()
    scrollToBottom()

  } catch (error: any) {
    let errorMsg = '麦克风测试失败'
    if (error.name === 'NotAllowedError') {
      errorMsg = '麦克风权限被拒绝，请在浏览器设置中允许访问'
    } else if (error.name === 'NotFoundError') {
      errorMsg = '未找到麦克风设备，请检查设备连接'
    }

    const testMessage: MessageItem = {
      id: Date.now(),
      text: `❌ ${errorMsg}`,
      time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
      isUser: false,
      isStreaming: false,
      htmlContent: `<div style="color: #EF4444; font-weight: 500;">❌ ${errorMsg}</div>`
    }
    messages.push(testMessage)
    await nextTick()
    scrollToBottom()
  }
}
</script>

<style scoped>
.chat-view {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background: #F8FAFC;
  font-family: 'Inter', sans-serif;
}

.chat-container {
  display: flex;
  flex: 1;
  overflow: hidden;
}

/* Sidebar Styles */
.sidebar {
  width: 320px;
  background: white;
  border-right: 1px solid #E2E8F0;
  display: flex;
  flex-direction: column;
  transition: width 0.3s ease;
}

.sidebar.collapsed {
  width: 0;
  overflow: hidden;
}

.sidebar.collapsed {
  width: 0;
  overflow: hidden;
}

.sidebar-header {
  padding: 16px;
  border-bottom: 1px solid #E2E8F0;
}

.sidebar-title {
  color: #1E293B;
  font-size: 18px;
  font-weight: 600;
  line-height: 28px;
  margin: 0 0 16px 0;
}

.search-box {
  position: relative;
  height: 38px;
}

.search-input {
  width: 100%;
  height: 100%;
  border: 1px solid #E2E8F0;
  border-radius: 8px;
  padding: 0 16px 0 40px;
  font-size: 14px;
  color: #1E293B;
  background: white;
  box-sizing: border-box;
}

.search-input::placeholder {
  color: #999999;
}

.search-input:focus {
  outline: none;
  border-color: #2C2C2C;
  box-shadow: 0 0 0 2px rgba(44, 44, 44, 0.1);
}

.search-icon {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.search-symbol {
  width: 12px;
  height: 12px;
  border: 2px solid #999;
  border-radius: 50%;
}

/* Chat List */
.chat-list {
  flex: 1;
  overflow-y: auto;
}

.chat-item {
  padding: 16px;
  border-bottom: 1px solid #F1F5F9;
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.chat-item:hover {
  background: #F8FAFC;
}

.chat-item.active {
  background: #F1F5F9;
  border-right: 2px solid #F1F5F9;
}

.chat-item-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.chat-title {
  color: #1E293B;
  font-size: 14px;
  font-weight: 500;
  line-height: 20px;
  margin: 0;
}

.chat-time {
  color: #64748B;
  font-size: 12px;
  font-weight: 400;
  line-height: 16px;
}

.chat-preview {
  color: #64748B;
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Sidebar Footer */
.sidebar-footer {
  padding: 16px;
  border-top: 1px solid #E2E8F0;
}

.new-chat-btn {
  width: 100%;
  height: 36px;
  background: black;
  color: white;
  border: none;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.3s ease;
}

.new-chat-btn:hover {
  background: #1a1a1a;
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

/* Main Chat Area */
.chat-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: #F8FAFC;
}

/* Chat Header */
.chat-header {
  height: 81px;
  background: white;
  border-bottom: 1px solid #E2E8F0;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 24px;
}

.chat-info {
  display: flex;
  align-items: center;
  gap: 12px;
}

.chat-avatar {
  width: 40px;
  height: 40px;
  background: #FAFCFF;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.avatar-icon {
  position: relative;
  width: 20px;
  height: 20px;
}

.bot-icon {
  position: absolute;
  top: 3px;
  left: 2px;
  width: 13px;
  height: 2px;
  background: black;
}

.bot-body {
  position: absolute;
  top: 7px;
  left: 8.06px;
  width: 9.94px;
  height: 11px;
  background: black;
  border-radius: 1px;
}

.chat-details h2 {
  color: #1E293B;
  font-size: 18px;
  font-weight: 600;
  line-height: 28px;
  margin: 0;
}

.chat-status {
  color: #64748B;
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  margin: 0;
}

.chat-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

.action-btn {
  width: 36px;
  height: 36px;
  background: transparent;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.2s ease;
}

.action-btn:hover {
  background: #F1F5F9;
}

.settings-icon {
  width: 20px;
  height: 20px;
  border: 2px solid #64748B;
  border-radius: 50%;
}

.user-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 2px solid #E2E8F0;
}

/* Messages Container */
.messages-container {
  flex: 1;
  overflow-y: auto;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.message {
  display: flex;
}

.bot-message {
  justify-content: flex-start;
}

.user-message {
  justify-content: flex-end;
}

.message-content {
  max-width: 500px;
  padding: 16px;
  border-radius: 16px;
  border: 1px solid #E2E8F0;
}

.bot-message .message-content {
  background: white;
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  border-bottom-right-radius: 16px;
  border-bottom-left-radius: 6px;
}

.user-message .message-content {
  background: black;
  color: white;
  border: none;
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  border-bottom-right-radius: 6px;
  border-bottom-left-radius: 16px;
}

.message-text {
  color: inherit;
  font-size: 14px;
  font-weight: 400;
  line-height: 22.75px;
  margin: 0 0 8px 0;
}

.message-time {
  font-size: 12px;
  font-weight: 400;
  line-height: 16px;
  opacity: 0.7;
}

.user-message .message-time {
  color: #9CA3AF;
}

.bot-message .message-time {
  color: #64748B;
}

/* Chat Input Container */
.chat-input-container {
  background: white;
  border-top: 1px solid #E2E8F0;
  padding: 24px;
}

.chat-modes {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
}

.mode-btn {
  height: 32px;
  padding: 0 12px;
  border-radius: 8px;
  border: none;
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.2s ease;
}

.mode-btn.active {
  background: black;
  color: white;
}

.mode-btn:not(.active) {
  background: #F1F5F9;
  color: #64748B;
}

.mode-btn:hover:not(.active) {
  background: #E2E8F0;
}

.mode-icon {
  width: 16px;
  height: 16px;
}

.chat-icon {
  border: 2px solid currentColor;
  border-radius: 2px;
}

.exercise-icon {
  border: 2px solid currentColor;
  border-radius: 1px;
}

.audio-icon {
  border: 2px solid currentColor;
  border-radius: 50%;
  position: relative;
}

.audio-icon::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 6px;
  height: 6px;
  background: currentColor;
  border-radius: 50%;
}

/* Input Box */
.input-box {
  display: flex;
  gap: 8px;
  align-items: center;
}

.message-input {
  flex: 1;
  height: 46px;
  border: 1px solid #E2E8F0;
  border-radius: 8px;
  padding: 0 16px;
  font-size: 14px;
  background: white;
  box-sizing: border-box;
}

.message-input:focus {
  outline: none;
  border-color: #2C2C2C;
  box-shadow: 0 0 0 2px rgba(44, 44, 44, 0.1);
}

.send-btn {
  width: 44px;
  height: 44px;
  background: #E2E8F0;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s ease;
}

.send-btn:hover:not(:disabled) {
  background: #CBD5E1;
  transform: translateY(-1px);
}

.send-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.send-icon {
  width: 16px;
  height: 16px;
  background: black;
  border-radius: 2px;
}

/* 录音按钮样式 */
.record-btn {
  min-width: 44px;
  height: 44px;
  background: #F1F5F9;
  border: 2px solid #E2E8F0;
  border-radius: 8px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 0 8px;
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
}

.record-btn:hover:not(:disabled) {
  background: #E2E8F0;
  border-color: #CBD5E1;
  transform: translateY(-1px);
}

.record-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.record-btn.recording {
  background: #FEE2E2;
  border-color: #EF4444;
  animation: recordingPulse 2s infinite;
}

@keyframes recordingPulse {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4);
  }
  50% {
    box-shadow: 0 0 0 8px rgba(239, 68, 68, 0);
  }
}

.record-icon {
  width: 16px;
  height: 16px;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}

.mic-icon {
  width: 8px;
  height: 12px;
  background: #64748B;
  border-radius: 8px 8px 0 0;
  position: relative;
  transition: all 0.3s ease;
}

.mic-icon::before {
  content: '';
  position: absolute;
  bottom: -4px;
  left: 50%;
  transform: translateX(-50%);
  width: 12px;
  height: 2px;
  background: #64748B;
  transition: all 0.3s ease;
}

.mic-icon::after {
  content: '';
  position: absolute;
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%);
  width: 2px;
  height: 4px;
  background: #64748B;
  transition: all 0.3s ease;
}

.record-icon.active .mic-icon {
  background: #EF4444;
}

.record-icon.active .mic-icon::before,
.record-icon.active .mic-icon::after {
  background: #EF4444;
}

.recording-time {
  font-size: 12px;
  font-weight: 500;
  color: #EF4444;
  min-width: 35px;
  text-align: center;
}

/* 录音时的声波动画 */
.sound-waves {
  position: absolute;
  right: -20px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  gap: 2px;
  align-items: center;
}

.wave {
  width: 2px;
  background: #EF4444;
  border-radius: 1px;
  animation: waveAnimation 1s ease-in-out infinite;
}

.wave1 {
  height: 8px;
  animation-delay: 0s;
}

.wave2 {
  height: 12px;
  animation-delay: 0.2s;
}

.wave3 {
  height: 6px;
  animation-delay: 0.4s;
}

@keyframes waveAnimation {
  0%, 100% {
    transform: scaleY(0.3);
    opacity: 0.5;
  }
  50% {
    transform: scaleY(1);
    opacity: 1;
  }
}

/* Markdown Content Styles */
.markdown-content {
  line-height: 1.6;
}

.markdown-content h1,
.markdown-content h2,
.markdown-content h3,
.markdown-content h4,
.markdown-content h5,
.markdown-content h6 {
  margin: 16px 0 8px 0;
  font-weight: 600;
}

.markdown-content h1 { font-size: 1.5em; }
.markdown-content h2 { font-size: 1.3em; }
.markdown-content h3 { font-size: 1.1em; }

.markdown-content p {
  margin: 8px 0;
}

.markdown-content ul,
.markdown-content ol {
  margin: 8px 0;
  padding-left: 20px;
}

.markdown-content li {
  margin: 4px 0;
}

.markdown-content blockquote {
  border-left: 4px solid #E2E8F0;
  padding-left: 12px;
  margin: 12px 0;
  color: #64748B;
  font-style: italic;
}

.markdown-content code {
  background: #F1F5F9;
  padding: 2px 4px;
  border-radius: 4px;
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  font-size: 0.9em;
}

.markdown-content pre {
  background: #F8FAFC;
  border: 1px solid #E2E8F0;
  border-radius: 8px;
  padding: 12px;
  margin: 12px 0;
  overflow-x: auto;
}

.markdown-content pre code {
  background: none;
  padding: 0;
  border-radius: 0;
  font-size: 0.85em;
}

.markdown-content table {
  border-collapse: collapse;
  width: 100%;
  margin: 12px 0;
}

.markdown-content th,
.markdown-content td {
  border: 1px solid #E2E8F0;
  padding: 8px 12px;
  text-align: left;
}

.markdown-content th {
  background: #F8FAFC;
  font-weight: 600;
}

.markdown-content a {
  color: #3B82F6;
  text-decoration: none;
}

.markdown-content a:hover {
  text-decoration: underline;
}

/* 用户消息中的Markdown样式适配 */
.user-message .markdown-content code {
  background: rgba(255, 255, 255, 0.1);
  color: #E5E7EB;
}

.user-message .markdown-content pre {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.1);
}

.user-message .markdown-content blockquote {
  border-left-color: rgba(255, 255, 255, 0.3);
  color: #D1D5DB;
}

.user-message .markdown-content th,
.user-message .markdown-content td {
  border-color: rgba(255, 255, 255, 0.1);
}

.user-message .markdown-content th {
  background: rgba(255, 255, 255, 0.05);
}

/* 习题保存按钮样式 */
.question-actions {
  margin: 12px 0 8px 0;
  display: flex;
  gap: 8px;
}

.save-question-btn {
  background: #22C55E;
  color: white;
  border: none;
  border-radius: 6px;
  padding: 8px 16px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  gap: 6px;
}
.message-content {
  max-width: 500px;
}
.lesson-plan-download-btn-container {
  margin-top: 12px; /* 按钮与上方内容的间距 */
  display: flex;
  justify-content: center; /* 按钮水平居中 */
  border: none; /* 确保没有边框 */
  padding: 0; /* 确保没有内边距 */
}

.download-doc-btn {
  width: 150px; /* 固定按钮宽度 */
  height: 40px; /* 固定按钮高度 */
  background-color: #16A34A;
  color: #fff;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  transition: all 0.2s ease;
}

.download-doc-btn:hover {
  background-color: #16A34A;
  transform: translateY(-1px);
}
.generated-image {
  /* 确保图片宽度不会超过容器宽度 */
  max-width: 100%;
  /* 保持图片的宽高比 */
  height: auto;
  /* 为图片添加圆角 */
  border-radius: 8px;
  /* 将图片作为块级元素显示，方便布局 */
  display: block;
  /* 为图片底部添加一些间距 */
  margin-bottom: 8px;
}
.generated-video {
  max-width: 100%;
  height: auto;
  border-radius: 8px;
  display: block;
  margin-bottom: 8px;
}

.generated-audio {
  max-width: 100%;
  height: auto;
  border-radius: 8px;
  display: block;
  margin-bottom: 8px;
}

.audio-container {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-start;
}

.download-audio-btn {
  background: #3B82F6;
  color: white;
  border: none;
  border-radius: 6px;
  padding: 6px 12px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  gap: 4px;
}

.download-audio-btn:hover {
  background: #2563EB;
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
}

.save-question-btn:hover {
  background: #16A34A;
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(34, 197, 94, 0.3);
}

.save-question-btn:active {
  transform: translateY(0);
}

/* 流式输出时的光标动画 */
.message[data-streaming="true"] .message-text::after {
  content: '▊';
  animation: blink 1s infinite;
  margin-left: 2px;
}

@keyframes blink {
  0%, 50% { opacity: 1; }
  51%, 100% { opacity: 0; }
}

/* 响应式设计 */
@media (max-width: 768px) {
  .sidebar {
    position: absolute;
    left: -320px;
    z-index: 10;
    transition: left 0.3s ease;
  }

  .sidebar.open {
    left: 0;
  }

  .chat-header {
    padding: 0 16px;
  }

  .messages-container {
    padding: 16px;
  }

  .chat-input-container {
    padding: 16px;
  }

  .message-content {
    max-width: 280px;
  }
}
</style>